package com.yutou.droid;

import android.app.Application;
import android.content.res.AssetManager;
import android.os.Build;

import com.yutou.droid.UselessCode.HookUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;
import dalvik.system.DexFile;

public class MyApplication extends Application {
    public static DexClassLoader loader;
    public static Application application;

    @Override
    public void onCreate() {
        super.onCreate();
        application=this;
        //注册hook工具，可实现无manifest注册启动Activity
        HookUtils hookUtils=HookUtils.init(this);
        if (true) {
            try {
                hookUtils.hookActivity();
                hookUtils.hookHandle();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        dexLoader();
    }
    private void dexLoader(){
        File path=new File("/data/data/"+getPackageName()+"/plugs");
        if(!path.exists()){
            path.mkdirs();
        }
        File jar=new File("/data/data/"+getPackageName()+"/plugs/gdtad.jar");
        if(!jar.exists()) {
            saveAdJar("gdtad.jar");
            return;
        }
        try{
            Object[] t1=getDexElements(getClassLoader()); //获取系统类加载器的dexElements数组
            loader=new DexClassLoader(jar.getAbsolutePath(),"/data/data/"+getPackageName()+"/plugs/",null,getClassLoader());
            Object[] t2=getDexElements(loader);  //获取jar包类加载器的dexElements数组
            saveLibPath(t1,t2); //合并两个数组，并将合并后的数组覆盖到系统类加载器
            removeLib(loader); //删掉jar包加载器的数组
            System.out.println("加载完成");
            getDexElements(getClassLoader());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 合并两个Elements数组并覆盖掉系统加载类的dexElements
     * @param t1 合成的Elements
     * @param t2 被合并的Elements
     * @throws Exception 未知异常
     */
    private void saveLibPath(Object[] t1,Object[] t2) throws Exception {
        //遍历BaseDexClassLoader，拿到pathList变量 源码参考： 46行
        //遍历BaseDexClassLoader源码：https://android.googlesource.com/platform/libcore/+/refs/heads/master/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
        //DexPathList类的源码：       https://android.googlesource.com/platform/libcore/+/refs/heads/master/dalvik/src/main/java/dalvik/system/DexPathList.java
        Field[] fields= BaseDexClassLoader.class.getDeclaredFields();
        Field pathListField = null;
        for (Field field : fields) {
            System.out.println(field.getName());
            if(field.getName().equals("pathList"))
            {
                pathListField=field;
            }
        }
        if(pathListField==null)
            return;
        pathListField.setAccessible(true);
        Object pathList=pathListField.get(getClassLoader());
        Field dexElements=pathList.getClass().getDeclaredField("dexElements"); //获取到系统类加载器的dexElements
        dexElements.setAccessible(true);
        //合并两个获取到系统类加载器的dexElements
        Object[] obj= Arrays.copyOf(t1,t1.length+t2.length);
        for (int i=t1.length,j=0;i<t1.length+t2.length;i++,j++){
            Array.set(obj,i,t2[j]);

        }
        //覆盖操作
        dexElements.set(pathList,obj);
    }

    /**
     * 移除类加载器的dexElements，不然会提示jar包有多个类加载器
     * @param loader 被移除dexElements的加载器
     */
    private void removeLib(ClassLoader loader){
        try{
            Field[] fields= BaseDexClassLoader.class.getDeclaredFields();
            Field pathListField = null;
            for (Field field : fields) {
                if(field.getName().equals("pathList"))
                    pathListField=field;
            }
            if(pathListField==null)
                return;
            pathListField.setAccessible(true);
            Object pathList=pathListField.get(loader);
            Field dexElements=pathList.getClass().getDeclaredField("dexElements");
            dexElements.setAccessible(true);
            dexElements.set(pathList,null);//设为null后就不要再使用这个加载器了，否则会报空指针
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     *获取类加载器的DexElements数组
     * @param loader 类加载器
     * @return dexElements
     */
    private Object[] getDexElements(ClassLoader loader) {
        System.out.println("===============start============");
        try {
            Field[] fields= BaseDexClassLoader.class.getDeclaredFields();
            Field pathListField = null;
            for (Field field : fields) {
                System.out.println(field.getName());
                if(field.getName().equals("pathList"))
                    pathListField=field;
            }
            if(pathListField==null)
                return  null;
            pathListField.setAccessible(true);
            Object pathList=pathListField.get(loader);
            Field dexElementsField=pathList.getClass().getDeclaredField("dexElements");
            dexElementsField.setAccessible(true);
            Object[]  dexElements= (Object[]) dexElementsField .get(pathList);
            //for仅仅只是用于打印Elements类信息的
            String pathStr="path";
            if(Build.VERSION.SDK_INT<Build.VERSION_CODES.O)
                pathStr="dir";
            if(Build.VERSION.SDK_INT<Build.VERSION_CODES.M)
                pathStr="file";
            for (Object lib : dexElements) {
                Field uri_F=lib.getClass().getDeclaredField(pathStr);
                Field localName_F=lib.getClass().getDeclaredField("dexFile");
                uri_F.setAccessible(true);
                localName_F.setAccessible(true);
                File path= (File) uri_F.get(lib);
                DexFile dexFile= (DexFile) localName_F.get(lib);
                System.out.println("uri = " + path.getAbsolutePath()+" localName = "+(dexFile==null));
            }
            System.out.println("dexElements length = "+dexElements.length);
            System.out.println("===============end============");
            return dexElements;
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============end============");
        return null;
    }

    /**
     * 从assets中释放jar包资源
     * @param jarName
     */
    private void saveAdJar(String jarName){
        AssetManager manager=this.getResources().getAssets();
        try {
            InputStream inputStream=manager.open(jarName);
            FileOutputStream outputStream=new FileOutputStream("/data/data/"+this.getPackageName()+"/plugs/"+jarName);
            if(outputStream==null){
                return;

            }
            int len=0;
            byte[] bytes=new byte[inputStream.available()];
            while ((len=inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
            }
            dexLoader();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
